package org.corey.dev.voxiverse.chunk;


import java.nio.FloatBuffer;
import java.util.Random;

import org.corey.dev.voxiverse.block.Block;
import org.corey.dev.voxiverse.block.BlockGen;
import org.corey.dev.voxiverse.body.Body;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;

public class Chunk {
	
	public static final int CHUNK_SIZE_X = 16;
	public static final int CHUNK_SIZE_Y = 92;
	public static final int CHUNK_SIZE_Z = 16;
	
	public static final int CUBE_LENGTH = 12;
	
	public Block[][][] blocks;
	private int VBOVertexHandle;
	private int VBOColorHandle;
	
	private Random r;
	
	public void Render(){
		GL11.glPushMatrix();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOVertexHandle);
		GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOColorHandle);
		GL11.glColorPointer(3, GL11.GL_FLOAT, 0, 0L);
		GL11.glDrawArrays(GL11.GL_QUADS, 0, CHUNK_SIZE_X * CHUNK_SIZE_Y
				* CHUNK_SIZE_Z * 24);

		GL11.glPopMatrix();
		
	}
	
	
	
	public Chunk(int offSetX, int offSetY, int offSetZ){
		r = new Random();
		
		//need to check if chunk is saved if so then load that, if not then create it
		blocks = new Block[CHUNK_SIZE_X * Body.WORLD_SIZE][CHUNK_SIZE_Y][CHUNK_SIZE_Z * Body.WORLD_SIZE];
		
		String filename = offSetX + "-" + (offSetZ); 
		
		
		boolean chunkExist = Save.CheckChunk(filename);
		
		if (chunkExist){
			System.out.println("Loading Chunk " + filename);
			Save.Load(blocks, filename);
			LoadedUnrender(offSetX, offSetY, offSetZ);
		} else {
			BlockGen.Generate(blocks, CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z, r);
			
			System.out.println("Saving Chunk " + filename);
			
			long startTime = System.currentTimeMillis();
			long endTime = 0;
			Unrender(offSetX, offSetY, offSetZ);
			Save.SaveChunk(blocks, filename);
			endTime = System.currentTimeMillis();
			long time = ((endTime - startTime) /1000);
			System.out.println(time);
			
		}
		
		
		//save the chunk into a file and then flush the array to save memory
	}
	
	public void Update(){
		
	}
	
	
	public void LoadedUnrender(float offSetX, float offSetY, float offSetZ){
		VBOColorHandle = GL15.glGenBuffers();
		VBOVertexHandle = GL15.glGenBuffers();
		
		FloatBuffer VertexPositionData = BufferUtils.createFloatBuffer( ((CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * 6 * 12));
		FloatBuffer VertexColorData = BufferUtils.createFloatBuffer((CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * 6 * 12);
		
		for (float x = 0; x < CHUNK_SIZE_X; x += 1){
			for (float y = 0; y < CHUNK_SIZE_Y; y += 1){
				for (float z = 0; z < CHUNK_SIZE_Z; z += 1){
					if (blocks[(int) x][(int) y][(int) z].IsActive()){
						VertexPositionData.put(CreateCube((float) (offSetX) + x * CUBE_LENGTH, (float) offSetY + y * CUBE_LENGTH, (float) (offSetZ) + z * CUBE_LENGTH));
			
						VertexColorData.put(CreateCubeVertexCol(GetCubeColor(blocks[(int) (x)][(int) (y)][(int) (z)])));
					}
				}
			}
		}
		
		VertexColorData.flip();
		VertexPositionData.flip();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOVertexHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexPositionData,
				GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOColorHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexColorData,
				GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
	}
	
	
	
	public void Unrender(float offSetX, float offSetY, float offSetZ){
		
		String filename = offSetX + "-" + (offSetZ);
		
		
		VBOColorHandle = GL15.glGenBuffers();
		VBOVertexHandle = GL15.glGenBuffers();
		
		FloatBuffer VertexPositionData = BufferUtils.createFloatBuffer( ((CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * 6 * 12));
		FloatBuffer VertexColorData = BufferUtils.createFloatBuffer((CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * 6 * 12);
		
		float x;
		float z;
		float y;
		
		x=0;
		filename = ((int) offSetX - (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH)) + "-" + ((int) offSetZ);
		for(z = 1; z<CHUNK_SIZE_Z - 1; z++){
			for (y = 1; y<CHUNK_SIZE_Y - 1;y++){
				if (blocks[(int) (x + 1)][(int) y][(int) z].GetID() == 0 | blocks[(int) (x)][(int) (y + 1)][(int) z].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z+1)].GetID() == 0 | blocks[(int) (x)][(int) (y-1)][(int) (z)].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z-1)].GetID() == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
					
				} else {
					if (Save.CheckNextChunk(CHUNK_SIZE_X - 1,(int)y ,(int)z ,filename) == 0){
						blocks[(int) x][(int) y][(int) z].SetActive(true);
					} else {
						blocks[(int) x][(int) y][(int) z].SetActive(false);
					}
				}
			}
		}
		
		z=0;
		filename = ((int)offSetX) + "-" + ((int)offSetZ - (Chunk.CHUNK_SIZE_Z*Chunk.CUBE_LENGTH));
		for (x = 1; x<CHUNK_SIZE_X - 1;x++){
			for (y = 1; y<CHUNK_SIZE_Y - 1;y++){
				if (blocks[(int) (x + 1)][(int) y][(int) z].GetID() == 0 | blocks[(int) (x)][(int) (y + 1)][(int) z].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z+1)].GetID() == 0 | blocks[(int) (x)][(int) (y-1)][(int) (z)].GetID() == 0 | blocks[(int) (x - 1)][(int) y][(int) (z)].GetID() == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
					
				} else {
					if (Save.CheckNextChunk((int) x ,(int)y ,CHUNK_SIZE_Z - 1 ,filename) == 0){
						blocks[(int) x][(int) y][(int) z].SetActive(true);
					} else {
						blocks[(int) x][(int) y][(int) z].SetActive(false);
					};
				}
			}
		}
		
		x = CHUNK_SIZE_X - 1;
		filename = ((int)offSetX + (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH)) + "-" + ((int)offSetZ);
		for(z = 1; z<CHUNK_SIZE_Z - 1; z++){
			for (y = 1; y<CHUNK_SIZE_Y - 1;y++){
				if (blocks[(int) (x - 1)][(int) y][(int) z].GetID() == 0 | blocks[(int) (x)][(int) (y + 1)][(int) z].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z+1)].GetID() == 0 | blocks[(int) (x)][(int) (y-1)][(int) (z)].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z-1)].GetID() == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
					
			} else {
				if (Save.CheckNextChunk(0 ,(int)y ,(int)z ,filename) == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
				} else {
					blocks[(int) x][(int) y][(int) z].SetActive(false);
				} 
			}
		}
		}
		
		z = CHUNK_SIZE_Z - 1;
		filename = ((int)offSetX) + "-" + ((int)offSetZ + (Chunk.CHUNK_SIZE_Z*Chunk.CUBE_LENGTH));
		for (x = 1; x<CHUNK_SIZE_X - 1;x++){
			for (y = 1; y<CHUNK_SIZE_Y - 1;y++){
				if (blocks[(int) (x + 1)][(int) y][(int) z].GetID() == 0 | blocks[(int) (x)][(int) (y + 1)][(int) z].GetID() == 0 | blocks[(int) (x - 1)][(int) y][(int) (z)].GetID() == 0 | blocks[(int) (x)][(int) (y-1)][(int) (z)].GetID() == 0 | blocks[(int) (x)][(int) y][(int) (z-1)].GetID() == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
					
			} else {
				if (Save.CheckNextChunk((int) x ,(int)y ,0 ,filename) == 0){
					blocks[(int) x][(int) y][(int) z].SetActive(true);
				} else {
					blocks[(int) x][(int) y][(int) z].SetActive(false);
				}
			}
		}
		}
		
		
		
		
		for (x = 0; x < CHUNK_SIZE_X; x += 1){
			for (z = 0; z < CHUNK_SIZE_Z; z += 1){
				for (y = 0; y < CHUNK_SIZE_Y; y += 1){
					//load chunks file next to it and read if the block is air or not to decide if block should be rendered
					
					if ( x == 0 | y ==0 | z == 0 | x == CHUNK_SIZE_X - 1 | y == CHUNK_SIZE_Y - 1 | z == CHUNK_SIZE_Z - 1){
					
						if ( x == 0){
							/*//load chunk array and check for same Y and Z coord and CHUNK_SIZE_X
							filename = ((int) offSetX - (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH)) + "-" + ((int) offSetZ);
							
							if (Save.CheckNextChunk(CHUNK_SIZE_X - 1,(int)y ,(int)z ,filename) == 0){
								blocks[(int) x][(int) y][(int) z].SetActive(true);
							} else {
								blocks[(int) x][(int) y][(int) z].SetActive(false);
							}*/
						}
					
						if ( y == 0){
							blocks[(int) x][(int) y][(int) z].SetActive(true);
							
						}
					
						if ( z == 0){
							/*filename = ((int)offSetX) + "-" + ((int)offSetZ - (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH));
							//load chunk array and check for same Y and X coord and CHUNK_SIZE_Z
							if (Save.CheckNextChunk((int) x ,(int)y ,CHUNK_SIZE_Z - 1 ,filename) == 0){
								blocks[(int) x][(int) y][(int) z].SetActive(true);
							} else {
								blocks[(int) x][(int) y][(int) z].SetActive(false);
							}*/
						}
					
						if ( x == CHUNK_SIZE_X - 1){
							/*filename = ((int)offSetX + (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH)) + "-" + ((int)offSetZ);
							//load chunk array and check for same Y and Z coord and 0 for X
							if (Save.CheckNextChunk(0 ,(int)y ,(int)z ,filename) == 0){
								blocks[(int) x][(int) y][(int) z].SetActive(true);
							} else {
								blocks[(int) x][(int) y][(int) z].SetActive(false);
							}*/
						}
					
						if ( z == CHUNK_SIZE_Z - 1){
							/*filename = ((int)offSetX) + "-" + ((int)offSetZ + (Chunk.CHUNK_SIZE_X*Chunk.CUBE_LENGTH));
							//load chunk array and check for same Y and X coord and 0 for Z
							if (Save.CheckNextChunk((int) x ,(int)y ,0 ,filename) == 0){
								blocks[(int) x][(int) y][(int) z].SetActive(true);
							} else {
								blocks[(int) x][(int) y][(int) z].SetActive(false);
							}*/
						}
					
					} else {
						
						int left = blocks[(int) (x - 1)][(int) (y)][(int) (z)].GetID();
						int right = blocks[(int) (x)][(int) (y - 1)][(int) (z)].GetID();
						int back = blocks[(int) (x + 1)][(int) (y)][(int) (z)].GetID();
						int front = blocks[(int) (x)][(int) (y + 1)][(int) (z)].GetID();
						int top = blocks[(int) (x)][(int) (y)][(int) (z + 1)].GetID();
						int bot = blocks[(int) (x)][(int) (y)][(int) (z - 1)].GetID();
						
						if (left == 0 | right == 0 | back == 0 | front == 0 | top == 0 | bot == 0){
							blocks[(int) x][(int) y][(int) z].SetActive(true);	
							
						} else {
							blocks[(int) x][(int) y][(int) z].SetActive(false);	
							
						}
						
						
					}
					
					
					
					if (blocks[(int) x][(int) y][(int) z].GetID() == 0){
						blocks[(int) x][(int) y][(int) z].SetActive(false);
					}
					
					if (blocks[(int) x][(int) y][(int) z].IsActive()){
						VertexPositionData.put(CreateCube((float) (offSetX) + x * CUBE_LENGTH, (float) offSetY + y * CUBE_LENGTH, (float) (offSetZ) + z * CUBE_LENGTH));
						
						VertexColorData.put(CreateCubeVertexCol(GetCubeColor(blocks[(int) (x)][(int) (y)][(int) (z)])));
					}
					
				}
			}
		}
		
		VertexColorData.flip();
		VertexPositionData.flip();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOVertexHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexPositionData,
				GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOColorHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexColorData,
				GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		
		
	}



	private float[] CreateCubeVertexCol(float[] CubeColorArray) {
		float[] cubeColors = new float[CubeColorArray.length * 4 * 6];
		for (int i = 0; i < cubeColors.length; i++) {
			cubeColors[i] = CubeColorArray[i % CubeColorArray.length];
		}
		return cubeColors;
	}
	
	public static float[] CreateCube(float x, float y, float z) {
		int offset = CUBE_LENGTH / 2;
		return new float[] {
				// BOTTOM QUAD(DOWN=+Y)
				x + offset, y + offset,
				z,
				x - offset,
				y + offset,
				z,
				x - offset,
				y + offset,
				z - CUBE_LENGTH,
				x + offset,
				y + offset,
				z - CUBE_LENGTH,
				// TOP!
				x + offset, y - offset, z - CUBE_LENGTH, x - offset,
				y - offset,
				z - CUBE_LENGTH,
				x - offset,
				y - offset,
				z,
				x + offset,
				y - offset,
				z,
				// FRONT QUAD
				x + offset, y + offset, z - CUBE_LENGTH, x - offset,
				y + offset, z - CUBE_LENGTH, x - offset,
				y - offset,
				z - CUBE_LENGTH,
				x + offset,
				y - offset,
				z - CUBE_LENGTH,
				// BACK QUAD
				x + offset, y - offset, z, x - offset, y - offset, z,
				x - offset, y + offset, z,
				x + offset,
				y + offset,
				z,
				// LEFT QUAD
				x - offset, y + offset, z - CUBE_LENGTH, x - offset,
				y + offset, z, x - offset, y - offset, z, x - offset,
				y - offset,
				z - CUBE_LENGTH,
				// RIGHT QUAD
				x + offset, y + offset, z, x + offset, y + offset,
				z - CUBE_LENGTH, x + offset, y - offset, z - CUBE_LENGTH,
				x + offset, y - offset, z };

	}

	private float[] GetCubeColor(Block block) {
		switch (block.GetID()) {
		case 0:
			return new float[] { 1, 0, 1 };
		case 1:
			return new float[] { 0, 1, 0 };

		case 2:
			return new float[] { 0.498f, 0.2f, 0 };
		case 3:
			return new float[] { 0, 0f, 1f };
		case 4:
			return new float[] { 0.753f, 0.753f, 0.753f};
		case 5:
			return new float[] {0.686f,0.275f,0.37f};
		case 6:
			return new float[] {1,0.914f,0.498f};
		case 7:
			return new float[] {0.25f,0.25f,0.25f};
		}
		return new float[] { 1, 1, 1 };
	}
	
	
	
	public static void EnableLighting(boolean enabled){
		GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
		GL11.glEnable(GL11.GL_COLOR_MATERIAL);
		GL11.glEnable(GL11.GL_LIGHTING);
	    GL11.glEnable(GL11.GL_LIGHT0);
	    
	}
	

}
